<template>
  <div class="bankers-algorithm">
    <div class="btnBox">
      <Button type="primary" ghost @click="showRequestFrom">请求资源</Button>
    </div>
    <Table :columns="columns" :data="data" :span-method="handleSpan"></Table>
    <Alert v-if="result.code === 1" type="success" show-icon>安全，安全序列为：{{ result.sequence }}</Alert>
    <Alert v-else-if="result.code === 0" type="error" show-icon>请求的资源数超出最大值,请重新输入</Alert>
    <Alert v-else-if="result.code === -1" type="error" show-icon>资源不足</Alert>
    <Alert v-else type="error" show-icon>不安全</Alert>

    
  </div>
</template>

<script>
export default {
  name: "bankers-algorithm",
  data() {
    // 进程
    const process = ["P0", "P1", "P2", "P3", "P4"]

    // 资源
    const resource = [10, 5, 7]

    // 可利用资源向量
    const available = [3, 3, 2]

    // 最大需求矩阵
    const max = [[7, 5, 3], [3, 2, 2], [9, 0, 2], [2, 2, 2], [4, 3, 3]]

    // 分配矩阵
    const allocation = [[0, 1, 0], [2, 0, 0], [3, 0, 2], [2, 1, 1], [0, 0, 2]]

    const _this = this
    return {
      result: {},
      process,
      resource,
      available,
      max,
      allocation,
      // works: [],
      selectProcess: '',
      requestResourceString: '',
      columns: [
        {
          title: '进程名',
          key: 'name'
        },
        {
          title: 'Max(A B C)',
          key: 'max',
          render(h, param) {
            return h('div', [
              h('Input', {
                props: {
                  value: param.row.max.join(' ').trim(),
                },
                nativeOn: {
                  change(e) {
                    let value = e.target.value
                    let valueArr = value.trim().split(' ')
                    if(valueArr.length != resource.length || valueArr.some(v => isNaN(v))) {
                      _this.$Modal.error({
                        title: "输入错误",
                        content: "您输入的格式错误，请按照正确格式输入：数字按空格隔开，不能输入非数字字符，只能输入3个数字分别对应ABC资源。"
                      });
                      e.target.value = param.row.max.join(' ').trim()
                    } else {
                      valueArr.forEach((v, i) => {
                        // _this.max[param.index][i] = v * 1
                        _this.$set(_this.max[param.index], i, v * 1)
                      })
                    }
                  }
                }
              })
            ])
          }
        },
        {
          title: 'Allocation(A B C)',
          key: 'allocation',
          render(h, param) {
            return h('div', [
              h('Input', {
                props: {
                  value: param.row.allocation.join(' ').trim(),
                },
                nativeOn: {
                  change(e) {
                    let value = e.target.value
                    let valueArr = value.trim().split(' ')
                    if(valueArr.length != resource.length || valueArr.some(v => isNaN(v))) {
                      _this.$Modal.error({
                        title: "输入错误",
                        content: "您输入的格式错误，请按照正确格式输入：数字按空格隔开，不能输入非数字字符，只能输入3个数字分别对应ABC资源。"
                      });
                      e.target.value = param.row.allocation.join(' ').trim()
                    } else {
                      valueArr.forEach((v, i) => {
                        _this.$set(_this.allocation[param.index], i, v * 1)
                      })
                    }
                  }
                }
              })
            ])
          }
        },
        {
          title: 'Need(A B C)',
          key: 'need'
        },
        {
          title: 'Available(A B C)',
          key: 'available'
        },
      ],
    };
  },
  methods: {
    /**
     * @param { Number } index 请求的进程名在进程集合中的索引
     * @param { Array } requestResource 请求的资源数
     * @returns { Object } code 1：成功，0：请求的资源数超出最大值， -1：资源不足, -2 不安全； sequence: 安全序列
     */
    request(index = 0, requestResource = []) {
      let equalt = requestResource.some((v, i) => v !== this.need[index][i])
      
      // 标识，1为成功，0为请求的资源数超出最大值， -1为资源不足
      let code = 1;
      let result = {code, sequence: []};
      for(let i = 0; i < requestResource.length; i++) {
        if(requestResource[i] <= this.need[index][i]) {
          if(requestResource[i] <= this.available[i]) {
            code = 1
          } else {
            code = -1
            break
          }
        } else {
          code = 0
          break
        }
      }
      
      if (code === 1) {
        requestResource.forEach((v, i) => {

          this.$set(this.available, i, this.available[i] - v)
          this.$set(this.allocation[index], i, this.allocation[index][i] + v)
        });
        result = this.detection()

        if(result.code === -2) {
          requestResource.forEach((v, i) => {
            this.$set(this.available, i, this.available[i] + v)
            this.$set(this.allocation[index], i, this.allocation[index][i] - v)
          });
          return result
        } else if(!equalt) {
          this.available = this.available.map((v, i) => v + this.allocation[index][i])
        }
        return result
      }
      return { code,  sequence: result.sequence }
    },

    /**
     * @return {Object} { code: 标识，1为成功，0为请求的资源数超出最大值， -1为资源不足, sequence: 安全序列 }
     */
    detection() {
      let work = [...this.available];
      let finish = [];
      let code = 1;
      let sequence = []
      for (let i = 0; i < this.process.length; i++) {
        finish[i] = false;
      }
      for (let i = 0; i < this.process.length; i++) {
        if (
          finish[i] === false &&
          !this.need[i].some((v, index) => v > work[index])
        ) {
          for (let j = 0; j < work.length; j++) {
            work[j] += this.allocation[i][j];
          }
          sequence.push(this.process[i])
          finish[i] = true;
          i = -1
        }
      }
      if (finish.some(v => v === false)) {
        // 不安全
        code = -2;
        sequence = []
      } else {
        // 安全
        code = 1;
      }
      return { code, sequence }
    },

    handleSpan({ rowIndex, columnIndex }) {
      if(rowIndex === 0 && columnIndex === 4) {
        return [this.process.length, 1]
      }
    },


    showRequestFrom() {
      this.$Modal.confirm({
        render: (h) => {
          return h('div', [
            h('Select', {
              style: {
                'margin-bottom': '20px'
              },
              props: {
                placeholder: '请求资源的进程',
                value: this.selectProcess
              },
              on: {
                'on-change': v => {
                  this.selectProcess = v
                }
              }
            }, this.process.map(item => {
              return h('Option', {
                props: {
                  value: item,
                }
              }, item)
            })),
            h('Input', {
              props: {
                placeholder: 'A B C各资源请求数，空格隔开',
                value: this.requestResourceString
              },
              on: {
                'on-change': e => {
                  this.requestResourceString = e.target.value
                },
                'on-blur': () => {

                  if(this.requestResourceArray.length !== this.resource.length || 
                     this.requestResourceArray.some(v => isNaN(v))) {
                       this.$Message.error({
                        content: "您输入的格式错误，请按照正确格式输入：数字按空格隔开，不能输入非数字字符，只能输入3个数字分别对应ABC资源。",
                        duration: 4,
                        background: true
                      });
                  }
                }
              }
            }, [
              h('span', {
                slot: 'prepend'
              }, '请求资源')
            ])
          ])
        },
        onOk: () => {
          let index = this.process.indexOf(this.selectProcess)
          // String ---> Number
          let result = this.request(index, this.requestResourceArray.map(v => v * 1))
          this.result = result 
          this.requestResourceString = '',
          this.selectProcess = ''
        },
        onCancel: () => {
          this.requestResourceString = '',
          this.selectProcess = ''
        }
      })
    }
  },
  created() {
    this.result = this.detection()
    
  },
  computed: {
    need() {
      const needArr = []
      for(let i = 0; i < this.process.length; i++) {
        needArr[i] = []
        for (let j = 0; j < this.resource.length; j++) {
          needArr[i][j] = this.max[i][j] - this.allocation[i][j]
        }
      }
      return needArr
    },
    data() {
      const dataArr = []
      for(let i = 0; i < this.process.length; i++) {
        dataArr.push({ name: this.process[i], max: this.max[i], allocation: this.allocation[i], need: this.need[i].join(' '), available: this.available.join(' ') })
      }
      return dataArr
    },
    requestResourceArray() {
      return this.requestResourceString.trim().split(' ')
    }
  },
};
</script>

<style lang="less" scope>
.bankers-algorithm {
    .ivu-btn {
      margin-top: 20px;
      margin-bottom: 20px;
    }
    .ivu-tabs {
      margin-top: 30px;
    }
    .btnBox {
      padding: 10px;
    }
  }
</style>